每天的專案會同步到 GitLab 上,可以前往 GitLab
查看,有興趣的朋友歡迎留言 or 來信討論,我的信箱是 nickchen1998@gmail.com。
在 Day 10 的時候,我們介紹了如何使用 LangChain 的 PyMuPDFLoader 讀取
PDF 文件,並將其轉換成向量寫入 Pinecone 的資料庫當中,不過那時我們使用的方式,是直接將 PDFLoader 按照頁數讀取後的內文直接做計算然後儲存,
而這次我們要來介紹如何使用 Chunking Strategy 來提升檢索段落的成效,會實際來看要怎麼要切割段落。
Chunking Strategy 是一種將文件內容切割成多個段落的策略,這樣做的好處是可以提升檢索的效率,因為當我們將文件內容切割成多個段落後,可以更精準地找到與查詢問題相關的段落,進而提升回答的準確度。
而 Chunking Strategy 可以分成兩大方向來討論,分別是「文章切割」以及「段落大小」來做討論,今天我們會針對文章切割的部分來做討論。
為什麼要使用 Chunking Strategy 呢?因為當文件內容過於龐大時,直接將整份文件作為檢索對象,會增加檢索的複雜度,並且可能導致檢索結果不夠精準。透過
Chunking Strategy,我們可以將文件內容切割成多個段落,讓檢索更加精準,提升回答的準確度。
以 OpenAI 提供的 GPT-4o 模型為例,他最大能接受 128,000 個 token 的輸入,如果參考的段落過長,可能會超過這個限制,而造成 API
請求失敗,即便可以把所有的段落都傳遞給模型,但是模型可能無法正確的理解太長的段落,越多的段落反而會越容易混淆模型,因此好的
Chunking Strategy 會大幅提升我們的檢索品質進而影響到回答的品質。
下面附上一些常見模型的最大 token 限制給大家參考:
模型名稱 | 最大 token 數 |
---|---|
GPT-3.5 turbo | 16,385 |
GPT-4o mini | 128,000 |
GPT-4o | 128,000 |
sonnet | 200,000 |
首先我們來介紹 Character Splitting,這是最簡單的方法,將文本以固定字元數分割,並且在相鄰的片段之間進行重疊。缺點是會忽略語句結構和語義。這種方法較少使用,因為容易導致上下文丟失。
讓我們快速看一下下面這段程式碼:
from env_settings import BASE_DIR
from langchain_community.document_loaders import PyMuPDFLoader
from langchain.text_splitter import CharacterTextSplitter
loader = PyMuPDFLoader(file_path=BASE_DIR / "勞動基準法.pdf")
first_page_content = loader.load()[0].page_content
spliter = CharacterTextSplitter(
chunk_size=100,
chunk_overlap=5
)
documents = spliter.create_documents([first_page_content])
print(documents[0].page_content)
可以看到擷取出的片段結果,基本快要等同於原本直接將資料讀取近來第一頁的原始內文,而造成這樣的結果,是因為 Character Splitting 會忽略語句結構和語義,導致切割的結果並不是我們想要的,這樣的方式並不會提升檢索的效率,因此我們需要更進階的方法來切割段落。
這是比較理想的初學者方法,強調保持語句結構的完整性。會依照段落、換行符號和空格等層次來進行分割,確保相關資訊盡可能地保持在同一片段中。
讓我們快速看一下下面這段程式碼:
from env_settings import BASE_DIR
from langchain_community.document_loaders import PyMuPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
loader = PyMuPDFLoader(file_path=BASE_DIR / "勞動基準法.pdf")
first_page_content = loader.load()[0].page_content
spliter = RecursiveCharacterTextSplitter(
chunk_size=100,
chunk_overlap=5
)
documents = spliter.create_documents([first_page_content])
print(documents[0].page_content)
讓我們看一下下面切割後的結果:
可以看到 Recursive Splitting 的結果,已經比 Character Splitting 來得好很多,開始可以按照文本內容的區塊來進行切割,並且切割的同時保持著段落的完整性。
實務上來說,最常用的方法是第二種 Recursive Splitting,因為這種方法可以保持語句結構的完整性,並且可以確保相關資訊盡可能地保持在同一片段中,這樣的方式可以提升檢索的效率,進而提升回答的準確度。明天我們會來看一篇論文,是有關讓語言模型替我們進行段落切割。